今天的主題是 Python 中的單元測試(Unit Testing)及自動化測試。測試是一個確保程式碼質量的重要步驟,特別是在開發過程中進行迭代更新時。這一課程將教你如何撰寫測試來確保程式碼的正確性,並學習 Python 中的 unittest
模組來進行單元測試。
單元測試是針對應用程式中的最小可測單元——通常是一個函數或方法——進行測試。它的目的是驗證這些最小單位是否按預期工作。
為什麼單元測試很重要?
unittest
模組進行單元測試Python 標準庫內建了 unittest
模組,讓我們能夠輕鬆撰寫單元測試。
首先,我們將撰寫一個簡單的函數,並使用 unittest
進行測試。
# 被測試的函數
def add(a, b):
return a + b
# 單元測試的範例
import unittest
class TestAddFunction(unittest.TestCase):
def test_add(self):
self.assertEqual(add(2, 3), 5) # 測試是否 2 + 3 等於 5
self.assertEqual(add(-1, 1), 0) # 測試是否 -1 + 1 等於 0
self.assertEqual(add(0, 0), 0) # 測試是否 0 + 0 等於 0
if __name__ == '__main__':
unittest.main()
在這裡,我們使用 unittest.TestCase
創建了一個測試類,並且在裡面定義了 test_add
方法來測試 add
函數。self.assertEqual
用來檢查實際結果是否等於預期結果。
unittest
提供了多種斷言方法來幫助我們驗證測試結果:
assertEqual(a, b)
: 測試 a 是否等於 bassertTrue(x)
: 測試 x 是否為真assertFalse(x)
: 測試 x 是否為假assertIsNone(x)
: 測試 x 是否為 NoneassertIn(a, b)
: 測試 a 是否在 b 中assertRaises(exc, fun, *args)
: 測試某個函數是否拋出指定的異常def divide(a, b):
if b == 0:
raise ValueError("不能除以 0")
return a / b
class TestDivideFunction(unittest.TestCase):
def test_divide(self):
self.assertEqual(divide(10, 2), 5)
self.assertRaises(ValueError, divide, 10, 0)
當你的程式有很多測試時,可以使用測試套件將多個測試組合起來,以便同時執行。這樣可以提高測試的組織性與效率。
def multiply(a, b):
return a * b
class TestMultiplyFunction(unittest.TestCase):
def test_multiply(self):
self.assertEqual(multiply(3, 5), 15)
# 創建測試套件
def suite():
suite = unittest.TestSuite()
suite.addTest(TestAddFunction('test_add'))
suite.addTest(TestMultiplyFunction('test_multiply'))
return suite
if __name__ == '__main__':
runner = unittest.TextTestRunner()
runner.run(suite())
我們可以結合 unittest
和持續集成工具(如 Jenkins、GitHub Actions)來自動化執行測試,當有新程式碼合併時,自動進行測試,確保沒有引入新的錯誤。
.github/workflows/python-app.yml
name: Python application
on:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.x'
- name: Install dependencies
run: pip install -r requirements.txt
- name: Run tests
run: python -m unittest discover
當你將程式碼推送到 GitHub 時,測試將自動運行。
練習 1: 撰寫一個函數,返回兩個數字的最大值,並為此函數撰寫單元測試。
練習 2: 創建一個簡單的字典查找程式,當查找不到指定的鍵時應拋出 KeyError
,並撰寫測試以檢查此行為。
這些練習涉及編寫簡單的函數並為其撰寫單元測試。單元測試用於驗證程式的行為是否符合預期,確保程式在未來的修改中能夠穩定運行。
unittest
模組來撰寫單元測試,確保該函數在各種情況下返回正確結果。def max_value(a, b):
return max(a, b)
import unittest
class TestMaxValue(unittest.TestCase):
def test_positive_numbers(self):
self.assertEqual(max_value(3, 5), 5)
def test_negative_numbers(self):
self.assertEqual(max_value(-3, -5), -3)
def test_equal_numbers(self):
self.assertEqual(max_value(4, 4), 4)
def test_mixed_sign_numbers(self):
self.assertEqual(max_value(-10, 5), 5)
# 運行單元測試
if __name__ == '__main__':
unittest.main()
max_value
函數簡單地使用內建的 max()
函數來返回兩個數中的最大值。unittest
模組,測試了不同情況下函數的輸出是否正確。KeyError
,並撰寫測試以檢查此行為KeyError
。unittest
模組來撰寫單元測試,檢查該函數是否正確拋出 KeyError
。def lookup(dictionary, key):
if key not in dictionary:
raise KeyError(f"Key '{key}' not found in dictionary.")
return dictionary[key]
import unittest
class TestLookupFunction(unittest.TestCase):
def setUp(self):
self.data = {"apple": 1, "banana": 2, "orange": 3}
def test_existing_key(self):
self.assertEqual(lookup(self.data, "apple"), 1)
def test_key_error(self):
with self.assertRaises(KeyError):
lookup(self.data, "grape")
# 運行單元測試
if __name__ == '__main__':
unittest.main()
lookup
函數檢查字典中是否存在指定的鍵,若不存在則拋出 KeyError
。setUp
方法來設置測試前的初始條件,並測試了兩種情況:查找存在的鍵和查找不存在的鍵。assertRaises
用於確認在查找不到鍵時正確地拋出了 KeyError
。這兩個練習展示了如何編寫基本函數並使用 unittest
進行單元測試,以驗證程式的正確性和穩定性。在實際開發中,這樣的測試能夠防止程式中的潛在錯誤並幫助程式進行維護和升級。
今天我們學習了如何在 Python 中撰寫單元測試並自動化測試流程。通過測試,我們能夠確保程式碼的穩定性和質量,並且在進行程式碼更新時可以自動檢測是否有新的錯誤出現。